package main

import (
	"fmt"
	"os"
	"os/signal"
	"syscall"

	"github.com/robfig/cron/v3"
	"github.com/spf13/cobra"
)

var (
	rootCmd = &cobra.Command{
		Use:   "croncli",
		Short: "定时器命令",
		RunE: func(cmd *cobra.Command, args []string) error {
			// check options
			if osType != "nx" && osType != "win" {
				return fmt.Errorf("ERROR: --os option must be [nx] or [win]")
			}

			if len(args) == 0 {
				return fmt.Errorf("ERROR: you should input a COMMAND or a SCRIPT-PATH to execute")
			}

			if mode != "" && mode != "delay" && mode != "skip" {
				return fmt.Errorf("ERROR: --mode option must be [delay] or [skip]")
			}

			cn := cron.New(cron.WithSeconds())
			job := NewCmdJob(osType, args)

			if repeat != "" {
				err := addJob(cn, fmt.Sprintf("@every %s", repeat), job)
				if err != nil {
					return err
				}
			}
			if crontab != "" {
				err := addJob(cn, crontab, job)
				if err != nil {
					return err
				}
			}

			cn.Start()

			sigs := make(chan os.Signal)
			signal.Notify(sigs, os.Interrupt, syscall.SIGTERM)

			fmt.Println("\r- Press Ctrl+C to terminate")
			for s := range sigs {
				fmt.Println(s)
				os.Exit(0)
			}
			return nil
		},
	}
	crontab   string
	repeat    string
	osType    string
	noRecover bool
	mode      string
)

func init() {
	rootCmd.Flags().StringVarP(&crontab, "cron", "c", "", `支持标准的crontab 表达式, ex. * * * * * *
各个 * 号的含义:
- 秒
- 分钟
- 小时
- 每月的第几天
- 月
- 每周的第几天`)
	rootCmd.Flags().StringVarP(&repeat, "repeat", "r", "", "重复规则, ex. 1s(每秒重复),1m(每分钟重复),1h(每小时重复)")
	rootCmd.Flags().StringVar(&osType, "os", "nx", "系统类型，nx=linux win=windows，默认nx")
	rootCmd.Flags().BoolVar(&noRecover, "norecover", false, "是否捕获任务的 panic 错误, 默认 false")
	rootCmd.Flags().StringVarP(&mode, "mode", "m", "", `任务执行模式:
- delay(如果上一次任务还未执行完成，因为耗时太长，则等待上一次任务完成之后再执行)
- skip(如果上一次任务还未完成，则跳过此次执行)`)
}

func addJob(cn *cron.Cron, c string, job cron.Job) error {
	var jobWrappers = make([]cron.JobWrapper, 0)
	if mode == "delay" {
		jobWrappers = append(jobWrappers, cron.DelayIfStillRunning(cron.DefaultLogger))
	}
	if mode == "skip" {
		jobWrappers = append(jobWrappers, cron.SkipIfStillRunning(cron.DefaultLogger))
	}

	if !noRecover {
		jobWrappers = append(jobWrappers, cron.Recover(cron.DefaultLogger))
	}

	_, err := cn.AddJob(c, cron.NewChain(jobWrappers...).Then(job))
	return err
}

func main() {
	Execute()
}

func Execute() {
	rootCmd.Execute()
}
